package com.youboy.oauth.model;

import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

import static com.youboy.oauth.common.OAuthCodec.*;

import org.springframework.util.StringUtils;

import com.youboy.oauth.common.OAuthParameters;

/**
 * 
 * @author loudyn
 * 
 */
public class HttpOAuthRequest extends HttpServletRequestWrapper implements OAuthRequest {
	private Set<String> supportedParameters;
	private Set<String> requiredParameters;

	/**
	 * 
	 * @param request
	 */
	public HttpOAuthRequest(HttpServletRequest request) {
		this(request, null);
	}

	public HttpOAuthRequest(HttpServletRequest request, Set<String> requiredParameters) {
		super(request);

		Set<String> supportedParameters = new TreeSet<String>();
		for (OAuthParameters parameter : OAuthParameters.values()) {
			supportedParameters.add(parameter.asString());
		}

		this.supportedParameters = supportedParameters;
		this.requiredParameters = requiredParameters;
	}

	private Map<String, String> parseHeader() {

		String oauthHeader = null;

		@SuppressWarnings("unchecked")
		Enumeration<String> headers = super.getHeaders("Authorization");
		while (headers.hasMoreElements()) {
			String headerValue = headers.nextElement();

			if (StringUtils.startsWithIgnoreCase(headerValue, "oauth")) {
				oauthHeader = headerValue;
				break;
			}
		}

		Map<String, String> result = null;
		if (null != oauthHeader) {

			result = new HashMap<String, String>();

			// Authorization : OAuth realm=""...
			String oauthHeaderValue = oauthHeader.substring(6);
			String[] oauthHeaderEntries = StringUtils.split(oauthHeaderValue, ",");

			for (String oauthHeaderEntry : oauthHeaderEntries) {
				String[] oauthKeyValuePair = StringUtils.delimitedListToStringArray(oauthHeaderEntry, "=", "\"");
				result.put(oauthDecode(oauthKeyValuePair[0]), oauthDecode(oauthKeyValuePair[1]));
			}
		}

		return result;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.youboy.oauth.model.OAuthRequest#getOAuthParameters()
	 */
	@Override
	public Map<String, String> getOAuthParameters() {
		Map<String, String> oauthParameters = parseHeader();

		if (null == oauthParameters) {

			oauthParameters = new HashMap<String, String>();

			for (String supportedParameter : supportedParameters) {
				String parameterValue = super.getParameter(supportedParameter);
				if (null != parameterValue) {
					oauthParameters.put(supportedParameter, parameterValue);
				}
			}
		}

		return Collections.unmodifiableMap(oauthParameters);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.youboy.oauth.model.OAuthRequest#getVerb()
	 */
	@Override
	public String getVerb() {
		return super.getMethod().toUpperCase();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.youboy.oauth.model.OAuthRequest#getSignatureBaseString()
	 */
	@Override
	public String getSignatureBaseString() {
		return null;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.youboy.oauth.model.OAuthRequest#validateParameters()
	 */
	@Override
	public void validateParameters() throws UnsupportedParameterException, MissingRequiredParameterException {

		if (null != requiredParameters) {

			@SuppressWarnings("unchecked")
			Map<Object, Object> parameterMap = super.getParameterMap();
			for (String requiredParameter : requiredParameters) {

				if (!parameterMap.containsKey(requiredParameter)) {
					throw new MissingRequiredParameterException(String.format("Required %s parameter!", requiredParameter));
				}
			}
		}
	}
}
